<?php
use frontend\modules\yii2\widgets\layouts\markdown\Markdown;
/* @var $this \yii\web\View */
$this->title = 'RESTful API路由设置-urlManager组件-Components组件';
$this->registerMetaTag([
    'name' => 'keywords',
    'content' => 'yii,yii2,框架,应用组件,Components,使用,教程,urlManager,路由机制,restful'
]);
$this->registerMetaTag([
    'name' => 'description',
    'content' => 'Yii专门给RESTFull API定制了路由机制，可以快速部署路由，并进行简单的定制。'
]);

$html = [
    "概述" => <<<HTML
# RESTful API专用路由设置 
RESTFul API是规范化的接口方案，因此有默认的路由模板，Yii给它专门设计了一个类来实现路由创建。我们在[api类的控制器](/docs/structure/controller/restful-api#route)中有讲过api路由的创建机制。

HTML,
    "config" => <<<HTML
## 配置举例 id=config
API路由类的好处就是您不用针对每个接口创建路由规则，可以使用`yii\\rest\UrlRule`类来批量创建：
```php
'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\\rest\UrlRule', 'controller' => 'user'],
    ],
],
```
上述配置与如下配置等效：
```php
'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'rules' => [
        'PUT,PATCH users/<id>' => 'user/update',
        'DELETE users/<id>' => 'user/delete',
        'GET,HEAD users/<id>' => 'user/view',
        'POST users' => 'user/create',
        'GET,HEAD users' => 'user/index',
        'users/<id>' => 'user/options',
        'users' => 'user/options',
    ],
],
```
HTML,
    "controller" => <<<HTML
## 控制器（controller） id=controller
控制器属性可以是字符串也可以是数字，表示需要针对哪个控制器批量创建路由：
```php
'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'rules' => [
        [
            'class' => 'yii\\rest\UrlRule', 
            'controller' => [
                'user' => 'user',         //针对user控制器创建路由表
                'post',         //针对post控制器创建路由表
                'api/article',  //针对api模块下的article控制器创建路由表
            ],
            'pluralize' => true // 开启接口的复数形式
        ],
    ],
],
```
[controller](#controller)属性有个和[pluralize](#pluralize)属性相悖的特性：当启用键值对的时候，会导致复数形式失效，在上述接口配置中，`user`采用了键值对，`post`没有使用键值对，因此接口调用就有如下区别：
```php
// 复数形式失效
http://hosthome/user
// 复数形式有效
http://hosthome/posts
```
HTML,
    "pluralize" => <<<HTML
## 接口复数形式（pluralize） id=pluralize
Yii对`yii\\rest\UrlRule`的设计有一个特性需要注意，就是创建的路由会以复数的形式出现，如：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user', //针对user控制器创建路由表
        ],
    ],
],
```
此时会创建的路由都会以`users`作为url：
```shell
// url 
http://hostname/users
```
如果要取消这个特性，使用`pluralize`属性即可：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user', // 针对user控制器创建路由表
        ],
        'pluralize' => false // 取消复数形式
    ],
],
```
当`controller`出现键值对的时候，复数形式会自动失效：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user' => 'user', // 针对user控制器创建路由表
        ],
        'pluralize' => true // 接口的复数形式会失效
    ],
],
```
此时接口应该为：
```shell
http://hosthome/user
```
HTML,
    "except" => <<<HTML
## 禁用路由（except） id=except
自动创建的路由并不是都会用到，此时可以加入`except`属性指定禁止创建的路由：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user', // 针对user控制器创建路由表
        ],
        'except' => ['options'] // 禁止创建options路由
    ],
],
```
`except`属性可以接受的值是由控制器id组成的数组，一般可为如下列表：
```php
'except' => ['update', 'delete', 'view', 'create', 'index', 'options'];
```
`except`的优先级会高于`only`属性，也就是说如下代码会禁止`create`路由的创建：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user', // 针对user控制器创建路由表
        ],
        'except' => ['create'],             // 禁止创建create路由
        'only' => ['create', 'update']      // 只创建create和update路由
    ],
],
```
HTML,
    "允许创建的路由" => <<<HTML
## 只允许的路由（only） id=only
和禁用（except）相对的有只允许（only）路由，顾名思义只创建你在`only`中定义的路由：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => [
            'user', // 针对user控制器创建路由表
        ],
        'only' => ['create'] // 只创建create路由
    ],
],
```
默认可以选择的动作id如下：
```php
'only' => ['update', 'delete', 'view', 'create', 'index', 'options'];
```
`only`的优先级比`except`要低，因此Yii不会这两个属性中重叠的路由。
HTML,
    "patterns" => <<<HTML
## 路由列表（patterns） id=patterns
在`patterns`属性中，罗列了可以创建的url，默认如下：
```php
public \$patterns = [
    'PUT,PATCH {id}' => 'update',
    'DELETE {id}' => 'delete',
    'GET,HEAD {id}' => 'view',
    'POST' => 'create',
    'GET,HEAD' => 'index',
    '{id}' => 'options',
    '' => 'options',
];
```
也就是说当我们定义了如下路由规则时，Yii会按照`patterns`逐行创建路由：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => 'user'
    ],
],
```
由于`patterns`是`public`权限的，因此您可以改变这种默认的行为：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => 'user',
        'patterns' => [
            'PUT,PATCH {id}' => 'update',
            'DELETE {id}' => 'delete',
            'GET,HEAD {id}' => 'view',
            'POST' => 'create',
            'GET,HEAD' => 'index',
            // '{id}' => 'options',
            // '' => 'options',
        ]
    ],
],
```
上述代码去除了`options`的接口，但如果是要扩展自定义的接口，更好的做法就是使用`extraPatterns`属性。
HTML,
    "extraPatterns" => <<<HTML
## 附加路由（extraPatterns） id=extraPatterns
当`patterns`提供的路由无法满足我们的需要时，我们可以使用`extraPatterns`来扩展路由，如下：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => 'user',
        'extraPatterns' => [
            'DELETE remove/{id}' => 'remove',
        ]
    ],
],
```
上述配置会在`patterns`的基础上创建一个新的路由：
```php
'rules' => [
    'DELETE users/remove/<id:\d[\d,]*>' => 'user/remove'
],
```
HTML,
    "prefix" => <<<HTML
## 接口前缀（prefix） id=prefix
这个属性可以给接口定义一个统一的前缀，通常进行接口组的划分及接口的规范化。

**不采用`prefix`属性的情况下，接口管理可能很混乱：**
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => ['user', 'v1/article'],
        'extraPatterns' => [
            'remove' => 'remove',
        ]
    ]
],
```
上述代码调用目标控制器的`remove`接口会有如下表现：
```shell
http://hosthome/users/remove
http://hosthome/v1/articles/remove
```
大家可以看到这两个接口的长度是不一样的，从接口中无法分清楚是第几版的接口，比较好的接口划分方法如下：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => ['user' => 'user', 'article' => 'v1/article'],
        'prefix' => 'v1',
        'extraPatterns' => [
            'remove' => 'remove',
        ]
    ]
],
```
此时接口的url就会变成：
```shell
http://hosthome/v1/user/remove
http://hosthome/v1/article/remove
```
> 注意：在使用`controller`的键值时，自动屏蔽接口的复数形式（pluralize）。

HTML,
    "tokens" => <<<HTML
## 正则匹配（tokens） id=tokens
我们通常可以在`urlManager`中直接使用正则匹配，如：
```php
'rules' => [
    'DELETE users/remove/<id:\d[\d,]*>' => 'user/remove'
],
```
但是正则表达式通常会很长，而且同样的正则表达式也通常会被用到很多地方，因此设计了`tokens`来使用自定义简化的字符串来代替正则表达式，默认的正则映射只有一条：
```php
public \$tokens = [
    '{id}' => '<id:\\\d[\\\d,]*>',
];
```
此时你可以使用`{id}`来代替正则表达式`<id:\\\d[\\\d,]*>`：
```php
'rules' => [
    [
        'class' => 'yii\\rest\UrlRule', 
        'controller' => 'user',
        'extraPatterns' => [
            'DELETE remove/{id}' => 'remove',
        ]
    ],
],
```
你也可以添加`tokens`映射：
```php
[
    'class' => 'yii\\rest\UrlRule',
    'controller' => ['device' => 'yii/device'],
    'prefix' => 'v1',
    'tokens' => [
        '{id}' => '<id:\\\d[\\\d,]*>',
        '{mac}' => '<mac:[0-9a-fA-F]{12}>'
    ],
    'extraPatterns' => [
        'remove/{mac}' => 'remove',
    ]
],
```
此时调用`remove`接口时就需要输入mac地址：
```shell
http://hosthome/v1/device/remove/2CF0EE1F5736
```
HTML,
    "" => <<<HTML

HTML,
];

echo Markdown::widget(["content" => implode("\n", $html)]);

